package org.dreamwork.cai.managed;

import org.dreamwork.cai.ServiceNotFoundException;
import org.dreamwork.cai.core.impl.AbstractRemoteServices;
import org.dreamwork.cai.core.impl.SRIObjectFactory;
import org.dreamwork.cai.srf.ServiceRouteFrame;
import org.dreamwork.cai.srf.ServiceRouteItem;
import org.dreamwork.cai.tools.Const;
import org.dreamwork.injection.impl.SimpleObjectContext;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class ObjectContextRemoteServices extends AbstractRemoteServices {
    private final Map<String, SimpleObjectContext> pool = Collections.synchronizedMap (new HashMap<> ());

    @Override
    public Object lookup (String namespace, String name) throws ServiceNotFoundException {
        if (!pool.containsKey (namespace)) {
            throw new ServiceNotFoundException ("namespace " + namespace + " not found");
        }

        SimpleObjectContext context = pool.get (namespace);
        Object target = context.getBean (name);
        if (target == null) {
            throw new ServiceNotFoundException ("service " + namespace + "." + name + " not found!");
        }
        return target;
    }

    @Override
    public <T> T lookup (String namespace, Class<T> type) throws ServiceNotFoundException {
        if (!pool.containsKey (namespace)) {
            throw new ServiceNotFoundException ("namespace " + namespace + " not found");
        }
        SimpleObjectContext context = pool.get (namespace);
        T target = context.getBean (type);
        if (target == null) {
            throw new ServiceNotFoundException ("service " + type.getName () + " not mapped.");
        }
        return target;
    }

    @Override
    public synchronized void register (ServiceRouteFrame frame) throws Exception {
        if (!frame.items.isEmpty ()) {
            SimpleObjectContext context = pool.computeIfAbsent (
                    frame.namespace,
                    key -> new SimpleObjectContext ()
            );

            patch (frame);
            for (ServiceRouteItem sri : frame.items.values ()) {
                context.create (new SRIObjectFactory (sri), sri.getJavaTypes ());
            }

            pool.put (frame.namespace, context);
            sendBroadcast (Const.SRF.BUILT, frame);
        }
    }

    @Override
    public synchronized void remove (String namespace) {
        if (pool.containsKey (namespace)) {
            SimpleObjectContext context = pool.get (namespace);
            context.dispose ();

            pool.remove (namespace);
            sendBroadcast (Const.SRF.DESTROY, namespace);
        }
    }
}